package ualrcdg.shared;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import ualrcdg.shared.exceptions.CDGException;

/**
 * Utility class which is responsible for providing access to CDG-specific DTDs to the XML parsing/validation methods which require them.
 */
class CDGEntityResolver implements EntityResolver
{
	final private HashMap< String, File > dtdMap = new HashMap< String, File >( );

	final Path dtdStorePath;
	
	/**
	 * Primary constructor
	 * 
	 * @param rootPath the base path at which the specified configuration file can be located, and to which the paths specified in that configuration file are relative 
	 * @param configFile the name of the configuration file which details the DTD lookup information for this CDGEntityResolver instance
	 * @throws FileNotFoundException If the specified configuration file cannot be located
	 * @throws IOException If an IO error occurs while the configuration file is being processed
	 * @throws CDGException If an error exists in the given configFile
	 */
	public CDGEntityResolver( Path rootPath, String configFile ) throws FileNotFoundException, IOException, CDGException
	{
		System.out.println( "CDGEntityResolver(" + rootPath + ", " + configFile + ")" );
		
		BufferedReader config = new BufferedReader( new FileReader( rootPath.resolve( configFile ).toFile( ) ) );

		String dtdRootFolder = config.readLine( );
		String id = null;
		String dtdFilename = null;

		dtdStorePath = rootPath.resolve( dtdRootFolder );

		while( ( id = config.readLine( ) ) != null && (dtdFilename = config.readLine( )) != null )
		{
			registerDTD( id, dtdFilename );
		}

		config.close( );
	}

	/**
	 * Attempts to register a SYSTEM ID / DTD filename pair with this CDGEntityResolver instance
	 * 
	 * @param systemId the entity name which should resolve to the specified DTD filename
	 * @param dtdFilename the filename used to validate XML files that reference the specified systemId
	 * @throws CDGException If this CDGEntityResolver instance already has a mapping for the specified SYSTEM ID
	 */
	/* LATER Consider allowing for dynamic DTD mapping / replacement (and add commit method) */
	private void registerDTD( String systemId, String dtdFilename ) throws CDGException
	{
		if( dtdMap.containsKey( systemId ) )
		{
			throw new CDGException( "Unable to register DTD with SYSTEM id [" + systemId+ "]." );
		}
		
		dtdMap.put( systemId, dtdStorePath.resolve( dtdFilename ).toFile( ) );
		
		System.out.println( "CDGEntityResolver: created mapping " + systemId + " -> " + dtdFilename );
	}
	
	/*
	 * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)
	 */
	@Override
	public InputSource resolveEntity( String publicId, String systemId ) throws SAXException, IOException
	{
		if( publicId != null )
		{
			throw new IllegalArgumentException( "CDGEntityResolver: cannot resolve PUBLIC[" + publicId + "]" );
		}
		
		File dtdFile = dtdMap.get( systemId );
		if( dtdFile == null )
		{
			throw new IOException( "CDGEntityResolver: Unable to resolve entity SYSTEM[" + systemId + "]" );
		}
		
		return new InputSource( new FileReader( dtdFile ) );
	}
}